/**
  ******************************************************************************
  * @file    bsp_eth_contiki_ip64_driver.c
  * @author  Iron
  * @date    2022-01-01
  * @version v1.0
  * @brief   bsp_eth_contiki_ip64_driver c file
  */

/** @addtogroup GROUP_TEMPLATE
  * @{
  */

/* Private includes ----------------------------------------------------------*/
#include "contiki.h"
#include "net/linkaddr.h"
#include "contiki-net.h"
#include "net/ip/uip.h"
#include "ip64/ip64.h"
#include "ip64/ip64-eth.h"
#include "bsp_eth_contiki_ip64_driver.h"

#define DEBUG 1
#include "net/ip/uip-debug.h"

#if DEBUG
void ethernet_printf_mac(struct uip_eth_addr *mac)
{
    PRINTF("%02X:%02X:%02X:%02X:%02X:%02X",
           mac->addr[0], mac->addr[1], mac->addr[2],
           mac->addr[3], mac->addr[4], mac->addr[5]);
}
#define PRINTF_ETH_MAC(mac)     ethernet_printf_mac(mac)
#else
#define PRINTF_ETH_MAC(mac)
#endif

/* Private define ------------------------------------------------------------*/
#define ETH_RX_EVT  0x01
#define ETH_TX_EVT  0x02

#define BUF             ((struct uip_eth_hdr *)&uip_buf[0])
#define IPBUF           ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_IP_BUF      ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])

/* Private macro -------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

PROCESS(ethernet_process, "Ethernet driver");

#define ETH_TX_BUFSIZE  UIP_BUFSIZE
#define ETH_RX_BUFSIZE (UIP_BUFSIZE * 2)

static volatile uint16_t eth_rxbuf_in = 0;
__ALIGNED(4) static uint8_t eth_rxbuf[ETH_RX_BUFSIZE];
__ALIGNED(4) static uint8_t eth_txbuf[ETH_TX_BUFSIZE];

/* Private function prototypes -----------------------------------------------*/

/*---------------------------------------------------------------------------*/
void bsp_eth_recv_irq_handle(uint16_t rxlen)
{
    uint32_t post_data = (eth_rxbuf_in << 16) | rxlen;

    process_post(&ethernet_process, ETH_RX_EVT, (void *)post_data);

    eth_rxbuf_in += rxlen;

    if (eth_rxbuf_in & 0x03)
    {
        eth_rxbuf_in &= ~0x03;
        eth_rxbuf_in += 0x04;   // 4 字节对齐
    }

    if ((eth_rxbuf_in + UIP_BUFSIZE) > ETH_RX_BUFSIZE)
    {
        eth_rxbuf_in = 0;
    }

    bsp_eth_rx_enable((uint32_t)&eth_rxbuf[eth_rxbuf_in], UIP_BUFSIZE);
}

/*---------------------------------------------------------------------------*/
static void bsp_eth_uip_send(void)
{
    if (uip_len <= UIP_BUFSIZE)
    {
        while (bsp_eth_get_tx_status() == BSP_ETH_ENABLE);
        memcpy(eth_txbuf, uip_buf, uip_len);
        bsp_eth_tx_enable((uint32_t)eth_txbuf, uip_len);
    }

#if DEBUG
    PRINTF("eth tx  ip: src=");
    PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
    PRINTF(" / dst=");
    PRINT6ADDR(&UIP_IP_BUF->destipaddr);
    PRINTF("\n");
    PRINTF("eth tx mac: src=");
    PRINTF_ETH_MAC(&BUF->src);
    PRINTF(" / dst=");
    PRINTF_ETH_MAC(&BUF->dest);
    PRINTF("\n");
#endif
}

/*---------------------------------------------------------------------------*/
#if NETSTACK_CONF_WITH_IPV6
static uint8_t
uip_output(const uip_lladdr_t *dest_mac)
{
    uip_len += sizeof(struct uip_eth_hdr);

    if (dest_mac == NULL)
    {
        /* broadcast packet */
        memset(&BUF->dest, 0xFF, sizeof(struct uip_eth_addr));
    }
    else
    {
        memcpy(&BUF->dest, dest_mac, sizeof(struct uip_eth_addr));
    }

    BUF->type = UIP_HTONS(UIP_ETHTYPE_IPV6);

    memcpy(&BUF->src, uip_lladdr.addr, sizeof(struct uip_eth_addr));

    bsp_eth_uip_send();

    return 0;
}
#endif /* NETSTACK_CONF_WITH_IPV6 */

static int
ip64_output(uint8_t *packet, uint16_t txlen)
{
    if (txlen <= UIP_BUFSIZE)
    {
        while (bsp_eth_get_tx_status() == BSP_ETH_ENABLE);
        memcpy(eth_txbuf, packet, txlen);
        bsp_eth_tx_enable((uint32_t)eth_txbuf, txlen);
        return txlen;
    }

    return -1;
}

/*---------------------------------------------------------------------------*/
static void
input(void)
{
    if (bsp_eth_get_rx_status() == BSP_ETH_DISABLE)
    {
        if (eth_rxbuf_in & 0x03)
        {
            eth_rxbuf_in >>= 2;
            eth_rxbuf_in = (eth_rxbuf_in + 1) << 2;   // 4 字节对齐
        }

        if ((eth_rxbuf_in + UIP_BUFSIZE) > ETH_RX_BUFSIZE)
        {
            eth_rxbuf_in = 0;
        }

        bsp_eth_rx_enable((uint32_t)&eth_rxbuf[eth_rxbuf_in], UIP_BUFSIZE);
    }
}

/*---------------------------------------------------------------------------*/
static void
init(void)
{
    static bool isInit = false;

    if (isInit)
        return;

    isInit = true;

//    uint8_t eui64[8];
//
//    /* Assume that linkaddr_node_addr holds the EUI64 of this device. */
//    memcpy(eui64, &linkaddr_node_addr, sizeof(eui64));
//
//    /* Mangle the EUI64 into a 48-bit Ethernet address. */
//    memcpy(&uip_lladdr.addr[0], &eui64[0], 3);
//    memcpy(&uip_lladdr.addr[3], &eui64[5], 3);

    /* In case the OUI happens to contain a broadcast bit, we mask that
       out here. */
    uip_lladdr.addr[0] = (uip_lladdr.addr[0] & 0xfe);

    /* Set the U/L bit, in order to create a locally administered MAC address */
    uip_lladdr.addr[0] = (uip_lladdr.addr[0] | 0x02);

    memcpy(ip64_eth_addr.addr, uip_lladdr.addr, sizeof(ip64_eth_addr.addr));

    bsp_eth_init(uip_lladdr.addr);

    PRINTF("MAC addr:");
    PRINTF_ETH_MAC(&uip_lladdr);
    PRINTF("\n");

    /*
     * Set out output function as the function to be called from uIP to
     * send a packet.
     */
    tcpip_set_outputfunc(uip_output);

    process_start(&ethernet_process, NULL);
}

/*---------------------------------------------------------------------------*/
PROCESS_THREAD(ethernet_process, ev, data)
{
    //static struct etimer e;

    PROCESS_BEGIN();

    if (bsp_eth_get_rx_status() == BSP_ETH_DISABLE)
    {
        input();
    }

    while (1)
    {
        //etimer_set(&e, 1);
        PROCESS_WAIT_EVENT();

        switch (ev)
        {
        case ETH_RX_EVT:
        {
            uint16_t rx_index = (((uint32_t)data) >> 16) & 0xFFFF;
            uint16_t rx_len = ((uint32_t)data) & 0xFFFF;
            uint8_t *rxbuf = &eth_rxbuf[rx_index];

            if (rx_len > 0 && rx_len <= UIP_BUFSIZE)
            {
                struct uip_eth_hdr *eth_head = (struct uip_eth_hdr *)&rxbuf[0];

                if (bsp_eth_get_rx_status() == BSP_ETH_DISABLE)
                {
                    input(); // 4字节对齐
                }

#if DEBUG
                PRINTF("eth rx    : eth_rxbuf_in=%d, rx_index=%d, rx_len=%d\n", eth_rxbuf_in, rx_index, rx_len);
                PRINTF("eth rx mac: src=");
                PRINTF_ETH_MAC(&eth_head->src);
                PRINTF(" / dst=");
                PRINTF_ETH_MAC(&eth_head->dest);
                PRINTF(" type: 0x%X", UIP_HTONS(eth_head->type));
                PRINTF("\n");
#endif

                if (eth_head->type == UIP_HTONS(UIP_ETHTYPE_IPV6))
                {
//                    uip_neighbor_add(&IPBUF->srcipaddr, (struct uip_neighbor_addr *)&BUF->src); // ipv4
                    uip_len = rx_len - sizeof(struct uip_eth_hdr);
                    memcpy(BUF, rxbuf, rx_len);
                    tcpip_input();
                }
                else
                {
                    memcpy(ip64_packet_buffer, rxbuf, rx_len);
                    IP64_INPUT(ip64_packet_buffer, rx_len);
                }
            }
        }
        break;

        case ETH_TX_EVT:
            break;

        default:
            break;
        }
    }

    PROCESS_END();
}

/*---------------------------------------------------------------------------*/
const struct ip64_driver ip64_eth_driver =
{
    init,
    ip64_output,
};

/*---------------------------------------------------------------------------*/
const struct network_driver uip_eth_driver =
{
    "uip",
    init,
    input
};


/**
  * @}
  */

/******************* (C)COPYRIGHT 2022 ***** END OF FILE *********************/
